import * as vscode from 'vscode';
import * as path from 'path';

/**
 * Extract action names using VS Code's built-in TypeScript language service
 * This approach doesn't require bundling the TypeScript compiler
 */
export class VSCodeASTParser {
  private static readonly TS_SERVER_READY_KEY = 'xlogicTips.tsServerReady';
  
  /**
   * Check if a symbol is a valid action symbol
   * Validates symbol kind and excludes invalid action names
   */
  private static isValidActionSymbol(symbol: vscode.DocumentSymbol): boolean {
    // Must be a method or property
    const isValidKind = symbol.kind === vscode.SymbolKind.Method || symbol.kind === vscode.SymbolKind.Property;
    
    // Must not be one of the excluded names
    const excludedNames = ['baseActions'];
    const isValidName = !excludedNames.includes(symbol.name);
    
    // Must be a valid JavaScript identifier
    const isValidIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(symbol.name);
    
    return isValidKind && isValidName && isValidIdentifier;
  }
  
  /**
   * Wait for TypeScript server to initialize then get document symbols
   * @param document VS Code text document
   * @param context VS Code extension context for caching server readiness
   * @param delayMs Delay in milliseconds (default: 5000ms)
   */
  private static async getDocumentSymbolsWithDelay(
    document: vscode.TextDocument, 
    context?: vscode.ExtensionContext, 
    delayMs: number = 5000
  ): Promise<vscode.DocumentSymbol[]> {
    // Check if TypeScript server is already ready (cached in context)
    const isServerReady = context?.globalState.get(this.TS_SERVER_READY_KEY, false);
    if (!isServerReady) {
      // Add configurable delay to wait for TypeScript server startup
      console.log(`Waiting ${delayMs}ms for TypeScript server to initialize...`);
      await new Promise(resolve => setTimeout(resolve, delayMs));
    } else {
      console.log('TypeScript server already ready (cached), skipping delay');
    }
    
    try {
      const symbols = await vscode.commands.executeCommand<vscode.DocumentSymbol[]>(
        'vscode.executeDocumentSymbolProvider',
        document.uri
      );
      
      if (symbols && symbols.length > 0) {
        // Cache the readiness status for future calls
        if (!isServerReady && context) {
          console.log(`TypeScript server ready, caching readiness status`);
          await context.globalState.update(this.TS_SERVER_READY_KEY, true);
        }
        console.log(`TypeScript server ready, found ${symbols.length} symbols for ${document.uri.fsPath}`);
        return symbols;
      } else {
        console.warn(`No symbols found for ${document.uri.fsPath} after ${isServerReady ? '0' : delayMs}ms TypeScript server delay`);
        return [];
      }
    } catch (error) {
      console.error(`Error getting symbols for ${document.uri.fsPath}:`, error);
      return [];
    }
  }
  
  /**
   * Get action names from a module file using VS Code's TypeScript service
   */
  static async getModuleActionNamesWithVSCode(
    filePath: string, 
    modelName: string, 
    innerActions: string[], 
    context?: vscode.ExtensionContext
  ): Promise<string[]> {
    try {
      // Open the document using VS Code's API
      const document = await vscode.workspace.openTextDocument(filePath);
      
      // Check if it's a valid TypeScript/JavaScript file
      if (!['typescript', 'javascript', 'typescriptreact', 'javascriptreact'].includes(document.languageId)) {
        console.warn(`Unsupported language for symbol parsing: ${document.languageId}`);
        return [];
      }
      
      // Wait for TypeScript server and get symbols
      const symbols = await this.getDocumentSymbolsWithDelay(document, context);
      
      if (!symbols || symbols.length === 0) {
        console.warn(`No symbols found for module ${filePath}`);
        return [];
      }
      
      console.log(`Processing ${symbols.length} symbols from module ${filePath}`);
      
      const actionNames: string[] = [];

      // set innerActions
      innerActions.forEach((innerActionName)=>{
        const actionName = modelName ? `${modelName}/${innerActionName}` : innerActionName;
        actionNames.push(actionName);
      });

      
      // Find the actions object in the symbols
      for (const symbol of symbols) {
        if (symbol.name === 'default' && symbol.kind === vscode.SymbolKind.Variable) {
          console.log(`Found default export with ${symbol.children?.length || 0} children`);
          // Look for actions property in the default export

          for (const child of symbol.children || []) {
            if (child.name === 'actions' && child.kind === vscode.SymbolKind.Property) {
              console.log(`Found actions property with ${child.children?.length || 0} actions`);
              // Extract action names from the actions object
              for (const action of child.children || []) {
                if (this.isValidActionSymbol(action)) {
                  const actionName = modelName ? `${modelName}/${action.name}` : action.name;
                  actionNames.push(actionName);
                  // console.log(`Extracted action: ${actionName}`);
                }
              }
            }
          }
        }
      }
      
      return actionNames;
    } catch (error) {
      console.error('Error extracting actions with VS Code service:', error);
      return [];
    }
  }
  
  /**
   * Get root store action names using VS Code's TypeScript service
   */
  static async getRootActionNamesWithVSCode(
    filePath: string, 
    context?: vscode.ExtensionContext
  ): Promise<string[]> {
    try {
      
      const document = await vscode.workspace.openTextDocument(filePath);
      
      // Check if it's a valid TypeScript/JavaScript file
      if (!['typescript', 'javascript', 'typescriptreact', 'javascriptreact'].includes(document.languageId)) {
        console.warn(`Unsupported language for symbol parsing: ${document.languageId}`);
        return [];
      }
      
      // Wait for TypeScript server and get symbols
      const symbols = await this.getDocumentSymbolsWithDelay(document, context);
      
      if (!symbols || symbols.length === 0) {
        console.warn(`No symbols found for root store ${filePath}`);
        return [];
      }
      
      console.log(`Processing ${symbols.length} symbols from root store ${filePath}`);
      
      const actionNames: string[] = [];
      
      // Find variables that contain actions
      for (const symbol of symbols) {
        if (symbol.kind === vscode.SymbolKind.Variable) {
          console.log(`Found variable: ${symbol.name} with ${symbol.children?.length || 0} children`);
          // Look for actions property
          for (const child of symbol.children || []) {
            if (child.name === 'actions' && child.kind === vscode.SymbolKind.Property) {
              console.log(`Found actions property with ${child.children?.length || 0} actions`);
              for (const action of child.children || []) {
                if (this.isValidActionSymbol(action)) {
                  actionNames.push(action.name);
                  console.log(`Extracted root action: ${action.name}`);
                }
              }
            }
          }
        }
      }
      
      return actionNames;
    } catch (error) {
      console.error('Error extracting root actions with VS Code service:', error);
      return [];
    }
  }
}